package thread;

/**
 * 懒汉模式
 * 1.先把构造方法改成私有的
 * 2.创建一个实例不进行初始化
 * 3.对外提供一个方法来获取这个实例，并进行初始化
 * 当外部调用这个方法时，肯定是需要用到这个实例，这时候再去进行初始化
 */

/**
 * 1.构造函数私有化
 * 2.正确加锁
 * 3.双重check(Double-check)
 * 4.volatile
 */
public class Singleton2 {
    /*
    //构造函数私有化
    private Singleton2(){}
    //创建实例，不进行初始化
    private static Singleton2 instance = null;
    //对外提供一个方法，获取该实例，并进行初始化
    public static Singleton2 getInstance(){
        //instance = new Singleton2();//只在第一次调用的时候初始化
        if(instance == null){ //读操作
            instance = new Singleton2(); //写操作
        }
        return instance;
    }
     */



    /**
     * 如果是两个线程同时调用
     * 那么两个线程都要对其进行初始化，所以要进行加锁
     * 要满足单例模式，加锁的对象一定是唯一的
     * 也就是一个程序里只有一个对象，才能保证多个线程访问时，对同一个对象进行加锁
     * 只需要第一个线程对该实例进行初始化，后面的线程，只需要直接获取前面这个实例就可以
     */

    /*
    //构造函数私有化
    private Singleton2(){}
    //创建实例，不进行初始化
    private static Singleton2 instance = null;
    //对外提供一个方法，获取该实例，并进行初始化
    public static Singleton2 getInstance() {
        //instance = new Singleton2();//只在第一次调用的时候初始化
        synchronized (Singleton2.class) { //类对象，一个程序只有一个
            if (instance == null) { //读操作
                instance = new Singleton2(); //写操作
            }
            return instance;
        }
    }
     */


    /**
     * 但是只要线程调用getInstance就会进行加锁，效率很低
     * 所以修改代码为instance==null的时候在进行加锁
     */
    //构造函数私有化
    private Singleton2(){}
    //创建实例，不进行初始化
    private static volatile Singleton2 instance = null;//禁止指令重排序
    //对外提供一个方法，获取该实例，并进行初始化
    public static Singleton2 getInstance() {
        //instance = new Singleton2();//只在第一次调用的时候初始化
        if (instance == null) {  //判断是否加锁
            synchronized (Singleton2.class) { //类对象，一个程序只有一个
                if (instance == null) { //读操作
                    instance = new Singleton2(); //写操作
                }
            }
        }
        return instance;
    }
}

